<template>
  <view class="calendar">
    <view class="calendar-title">
      <view>日</view>
      <view>一</view>
      <view>二</view>
      <view>三</view>
      <view>四</view>
      <view>五</view>
      <view>六</view>
    </view>

    <scroll-view class="calendar-main" scroll-y @scrolltoupper="onScrollToUpper" @scrolltolower="onScrollToLower">
      <view class="calendar-month" v-for="month in monthList" :key="month.year + '-' + month.month">
        <view class="calendar-month-title">{{ `${month.year} 年 ${month.month + 1} 月` }}</view>
        <view class="calendar-month-date">
          <view v-for="i in new Date(month.year, month.month, 1).getDay()" :key="-i"></view>
          <view
            v-for="date in getMonthDaysNumber(month.month, month.year)"
            :key="date"
            :class="[
              'calendar-month-date-item',
              {
                'is-today': month.year === todayYear && month.month === todayMonth && date === todayDate,
                'has-event': hasEvent(month.year, month.month, date),
              },
            ]"
          >
            <text class="calendar-month-date-item-prepend">{{ isFestival(month.month, date) }}</text>
            <text>{{ date }}</text>
            <text class="calendar-month-date-item-append">{{ hasEvent(month.year, month.month, date) }}</text>
          </view>
        </view>
      </view>
    </scroll-view>

    <view class="remark">
      <view>说明：</view>
      <view>该日历组件支持无限滚动，所有日期均为动态生成。</view>
      <view>节日和事件支持动态添加，方便扩展。</view>
      <view>基于纯 JS 实现，不依赖任何第三方库。</view>
      <view>可以改进的地方：</view>
      <view>后续可以使用虚拟滚动，以提高性能。</view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      monthList: [],

      todayYear: '',
      todayMonth: '',
      todayDate: '',

      schedule: [
        { info: '入住', year: 2023, month: 8, date: 11 },
        { info: '离店', year: 2023, month: 8, date: 23 },
      ],

      festivals: [
        { info: '父亲节', month: 5, date: 18 },
        { info: '端午节', month: 5, date: 22 },
        { info: '中秋节', month: 8, date: 29 },
        { info: '国庆节', month: 9, date: 1 },
        { info: '重阳节', month: 9, date: 23 },
      ],
    }
  },

  onLoad() {
    this.todayYear = new Date().getFullYear()
    this.todayMonth = new Date().getMonth()
    this.todayDate = new Date().getDate()

    this.monthList.push({ year: this.todayYear, month: this.todayMonth })
    this.onScrollToLower()
    // this.onScrollToUpper()

    this.$nextTick(() => {
      document.querySelector('.is-today').scrollIntoView()
    })
  },

  methods: {
    // 获取上一个月
    getPrevMonth(m, y) {
      let month = m || 11
      let year = y
      year -= m === 0
      month -= m !== 0
      return { year, month }
    },

    // 获取下一个月
    getNextMonth(m, y) {
      let month = m
      let year = y
      year += m === 11
      month = (1 + month) % 12
      return { year, month }
    },

    // 获取某年某月的天数
    getMonthDaysNumber(month, year) {
      // 判断是否为闰年
      const isLeap = year % 100 === 0 ? (year % 400 === 0 ? 1 : 0) : year % 4 === 0 ? 1 : 0
      const monthDay = [31, 28 + isLeap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] // 数组中的每一项代表每个月的天数
      return monthDay[month]
    },

    onScrollToUpper() {
      this.monthList.unshift(this.getPrevMonth(this.monthList[0].month, this.monthList[0].year))
    },

    onScrollToLower() {
      this.monthList.push(this.getNextMonth(this.monthList[this.monthList.length - 1].month, this.monthList[this.monthList.length - 1].year))
    },

    hasEvent(year, month, date) {
      return this.schedule.find(item => year === item.year && month === item.month && date === item.date)?.info
    },

    isFestival(month, date) {
      return this.festivals.find(item => month === item.month && date === item.date)?.info
    },
  },
}
</script>

<style lang="scss">
.calendar {
  max-width: 500px;
  margin: 0 auto;

  &-title {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    // gap: 20rpx;
    padding: 20rpx;

    text-align: center;
    background-color: #f8f8f8;
  }

  &-main {
    height: 700rpx;
  }

  &-month {
    &-title {
      padding: 30rpx;
    }

    &-date {
      display: grid;
      grid-template-columns: repeat(7, 1fr);
      // gap: 20rpx;
      padding: 20rpx;

      // text-align: center;

      &-item {
        // padding: 30rpx 0;
        aspect-ratio: 1/1;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 28rpx;

        position: relative;

        &-prepend {
          position: absolute;
          top: 6rpx;
          left: 0;
          right: 0;
          text-align: center;
          font-size: 24rpx;
          transform: scale(0.8);
        }

        &-append {
          position: absolute;
          bottom: 6rpx;
          left: 0;
          right: 0;
          text-align: center;
          font-size: 24rpx;
          transform: scale(0.8);
        }
      }

      .is-today {
        color: white;
        background-color: black;
        border-radius: 50%;
      }

      .has-event {
        color: white;
        background-color: rgb(90, 60, 150);
        border-radius: 10rpx;
      }
    }
  }

  .remark {
    padding: 30rpx;

    display: grid;
    gap: 20rpx;
  }
}
</style>
